/*
	Copyright (c) 2008 TPSi
	Alex Iskander
	
	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

*/

/**
	\file Bound.h
	\brief Declares the BindProperty class.
*/

#ifndef BINDABLE_BINDABLE_BOUND_H_
#define BINDABLE_BINDABLE_BOUND_H_
#include "Bindable.h"
#include "Bindable.h"
#include "Converter.h"
#include "Functions/Getter.h"
#include "Functions/Setter.h"
#include <iostream>
#include <vector>
#include <string>
#include <iostream>

namespace Bind
{
	/* Forward Declarations */
	class Bindable;

	/**
	 \class Bound
	 \brief A template class allowing creation of automatically binding
	 properties.
	*/
	template<typename PropertyType> class Bound : public Bindable
	{
	public:
		/**
			\fn Bound()
			\brief Creates a BindableObject property. Usually, you would have no need
			to call directly.
			\par Example:
			@code
class MyClass
{
public:
	Bound<string> name; //called implicitly
	Bound<int> id; //called implicitly.
};
			@endcode
		*/
		Bound();
		
		Bound(Bound<PropertyType> &property);
		
		Bound(PropertyType value);
		
		
		
		/**
		\fn operator=(PropertyType value);
		\brief A convenience function allowing you to set the value of the property using the
		assignment operator.
		*/
		Bound<PropertyType> &operator=(PropertyType value);
		
		/**
		 \fn operator=(Bindable &value);
		 \brief Binds to a property.
		 */
		Bound<PropertyType> &operator=(Bindable &value);
		
		/**
		 \fn operator=(Bound<PropertyType> &value);
		 \brief Binds to another Bound property.
		 */
		Bound<PropertyType> &operator=(Bound<PropertyType> &value);
		
		
		/**
		 \fn unset();
		 \brief Re-initializes the value (doesn't ensure the value is safe),
		 and sets isSet to false.
		 */
		void unset();
		
		/**
		 \fn isSet() const;
		 \brief Returns a boolean specifying if the property has been set.
		 */
		bool isSet() const;
		
		
		/**
		 \fn biBind(Bound<PropertyType> &value);
		 \brief Binds bi-directionally to another Bound property.
		 */
		void biBind(Bound<PropertyType> &value);
		
		
		
		/**
		\fn get() const;
		\brief Returns the value of the property.
		*/
		PropertyType get() const;
		
		/**
		 \fn convert(BaseConverter<PropertyType> *converter);
		 \brief Sets the converter to use.
		 
		 Returns a reference to this property.
		 */
		Bound<PropertyType> &convert(BaseConverter<PropertyType> *converter);
		
		
		/**
		 \fn isBindable();
		 \brief Returns if this is (or represents) a BindableObject object.
		 
		 Accompanies getBindable().
		 */
		bool isBindable();
		
		/**
		 \fn getBindable();
		 \brief Gets the BindableObject object that this represents.
		 */
		BindableObject *getBindable();

		/**
		 \fn isWeakBind();
		 \brief Returns true if the bind is weak.
		 */
		bool isWeakBind();
		
		/**
		 \fn updated(Bindable *property);
		 \brief Receives updates.
		 */
		void updated(Bindable *property);
		
		
		/**
			\fn operator PropertyType() const;
			\brief A function which allows casting from this type to PropertyType.
		*/
		operator PropertyType() const;
	
	
		/**
		 \fn operator-> ();
		 \brief Allows you to use the bound property as if it were any
		 other object.
		 */
		PropertyType operator->();
		
		
		/**
		 \fn operator==(Bound<PropertyType> &p) const;
		 \brief Returns true if the values of the properties are equal.
		 
		 Will not work properly for weakly bound properties.
		 */
		bool operator == (Bound<PropertyType> &p) const;
		
		/**
		 \fn operator==(PropertyType p) const;
		 \brief Returns true if the value of the property is equal to the value
		 specified.
		 */
		bool operator == (PropertyType p) const;
		
		~Bound();
		
		/*
		 Getter and Setter
		 */
		Getter<PropertyType> getter;
		Setter<PropertyType> setter;
		
	protected:
		/**
		 \fn onBind(Bindable *property, bool copy);
		 \brief Called when binding occurs.
		 */
		void onBind(Bindable *property, bool copy);
		
		
		/**
		 \fn onUnbind(Bindable *property);
		 \brief Called when unbinding occurs.
		 */
		void onUnbind(Bindable *property);
		
		/**
		 \var _converter
		 \brief The converter to use to convert values to the current type.
		 
		 The converter should convert either this object or the object this
		 is connected to into the result type. The result of the conversion will
		 be what is returned through "get" and other functions.
		 
		 If converter is NULL, performance will be higher, as it will not do
		 any conversion.
		 */
		BaseConverter<PropertyType> *_converter;
		
		/**
		 \fn _setValue(PropertyType value);
		 \brief Sets the value at the lowest level (no disconnecitons or anything)
		 */
		void _setValue(PropertyType value);
		
		/**
		 \fn getDefaultValue();
		 \brief Returns a default value. For example, Integers would have 0,
		 booleans false, etc. Usually only needed for primitive types which
		 lack proper constructors.
		 
		 Note: pointers default to NULL.
		 */
		PropertyType getDefaultValue();

	private:
		/**
		 \fn init();
		 \brief Runs initialization functions.
		 */
		void init();
				
		Bound<PropertyType> *strongBind;
		//!< The property (if any) this BindProperty is currently bound to.
		
		bool disableAutoDisconnect;
		//!< If true, the property does not disconnect from the property
		//!< it is bound to when its value is changed.
		
		PropertyType value; //!< The value of this property.
		
		bool weakBind;
		
		bool _isSet; //!< Marks if the value has been set.
		
	};

	template<typename PropertyType> void
	Bound<PropertyType>::init()
	{
		//initialize value
		this->value = getDefaultValue();
		
		//initialize boundTo
		this->boundTo = NULL;
		
		//initialize isUpdating
		this->isUpdating = false;
		
		//there is no weak bind or normal bind
		this->weakBind = false;
		this->strongBind = NULL;
		
		//and there is no converter
		this->_converter = NULL;
		
		//and no value
		this->_isSet = false;
		
		//initialize disableAutoDisconnect
		this->disableAutoDisconnect = false;
	}
	
	/* Implementation */
	template<typename PropertyType> Bound<PropertyType>::Bound() : value()
	{
		//just call initializer
		this->init();
	}
	
	template<typename PropertyType> 
	Bound<PropertyType>::Bound(Bound<PropertyType> &property) : value()
	{
		//call initializer
		this->init();
		
		//initialize binding
		(*this) = property;
	}
	
	template<typename PropertyType> 
	Bound<PropertyType>::Bound(PropertyType value) : value()
	{
		//call initializer
		this->init();
		
		//initialize value
		(*this) = value;
	}
	
	/* Default values */
	template<typename PropertyType> inline PropertyType
	_getDefaultValue(PropertyType example)
	{
		return PropertyType();
	}
	
	template<typename PropertyType> inline PropertyType *
	_getDefaultValue(PropertyType *example)
	{
		return NULL;
	}
	
	template<typename PropertyType> inline PropertyType
	Bound<PropertyType>::getDefaultValue()
	{
		return _getDefaultValue<PropertyType>(this->value);
	}

	template<> inline int
	Bound<int>::getDefaultValue()
	{
		return 0;
	}
	
	template<> inline double
	Bound<double>::getDefaultValue()
	{
		return 0;
	}

	
	template<> inline bool
	Bound<bool>::getDefaultValue()
	{
		return 0;
	}
	

	template<typename PropertyType> void
	Bound<PropertyType>::onBind(Bindable *property, bool copy)
	{
		// we don't know if we are a weak link yet...
		this->weakBind = false;
		
		//assume that we should auto-disconnect (if not, we'll be told otherwise later)
		this->disableAutoDisconnect = false;
		
		/* If it is not of the same type, make a weak link */
		Bound<PropertyType> *bindTo = dynamic_cast< Bound<PropertyType> *> (property);
		if (!bindTo)
		{
			/* This is a "weak" bind - we have no idea what the value is,
			   how to get it, or what to do with it. Better have a converter.
			 */
			this->weakBind = true;
			this->strongBind = NULL;
		}
		else
		{
			/* 
			 This is a "strong" bind. We are bound to something like us, and
			 can handle everything directly and easily.
			 */
			this->strongBind = bindTo;
			
			//set converter if we should copy things
			if (copy && !this->_converter)
				this->_converter = this->strongBind->_converter;
			
		}
		
		// update value
		this->updated(this->boundTo);
	}
	
	template<typename PropertyType> void
	Bound<PropertyType>::onUnbind(Bindable *property)
	{
		// set weakBind to false - there is no bind.
		this->weakBind = false;
		
		//remove strong bind
		this->strongBind = NULL;
		
		//don't reset value; this could have been a bake.
	}
	
	template<typename PropertyType> void
	Bound<PropertyType>::updated(Bindable *property)
	{

		//check if the event is triggered from the right property
		if (property == this->boundTo)
		{
			/* If we have a strong bind, check for if the property is set */
			if (this->strongBind)
			{
				this->_isSet = this->strongBind->isSet();
			}
			
			/*
			 With weak binds, there is only one way to get the value: conversion.
			 With strong binds, we can set it directly, or convert it if we have
			 a converter.
			 */
			if (this->_converter)
			{
				//get the value through conversion
				this->_setValue(this->_converter->convert(this->boundTo));
			}
			else if (property == this->strongBind)
			{
				// if it is not a weak bind and there is no converter, set the
				// value directly.
				this->_setValue(this->strongBind->get());
			}
		}

		//notify others
		Bindable::updated(property);
	}
	
	
	template<typename PropertyType> Bound<PropertyType> &
	Bound<PropertyType>::operator=(PropertyType value)
	{
		/* Remove existing bindings if auto-disconnect enabled */
		if (!this->disableAutoDisconnect) this->bake();
		
		/* Set Value */
		this->_setValue(value);
		
		/* Mark that we have a value */
		this->_isSet = true;
		
		/* We have updated */
		this->update();
		
		//return *this
		return *this;
	}
	
	
	template<typename PropertyType> Bound<PropertyType> &
	Bound<PropertyType>::operator=(Bindable &value)
	{
		//set binding (and copy)
		this->set(value, true);
		
		//return *this
		return *this;
	}
	
	template<typename PropertyType> Bound<PropertyType> &
	Bound<PropertyType>::operator=(Bound<PropertyType> &value)
	{
		//set binding (and copy)
		this->set(value, true);
		
		//return *this
		return *this;
	}
	
	template<typename PropertyType> void
	Bound<PropertyType>::unset()
	{
		//don't do anything if it isn't set (it would just be wasting time)
		if (this->_isSet)
		{
			//bake to get rid of bindings
			this->bake();
			
			//re-initialize the value
			this->_setValue(getDefaultValue());
			
			//mark isSet to false.
			this->_isSet = false;
			
			//update
			this->update();
		}
	}	
	
	template<typename PropertyType> inline bool
	Bound<PropertyType>::isSet() const
	{
		//return _isSet, the internal variable representing if it is set or not.
		return this->_isSet;
	}
	
	
	template<typename PropertyType> void
	Bound<PropertyType>::biBind(Bound<PropertyType> &value)
	{
		this->set(value);
		value.set(*this);
		
		this->disableAutoDisconnect = true;
		value.disableAutoDisconnect = true;
	}

	template<typename PropertyType> inline PropertyType
	Bound<PropertyType>::get() const
	{
		/* If we have a getter, use it */
		if (this->getter.isSet())
		{
			return this->getter(this);
		}
		
		//otherwise, return value
		return this->value;
	}
	
	template<typename PropertyType> inline void
	Bound<PropertyType>::_setValue(PropertyType value)
	{
		/* If we have a setter, use it */
		if (this->setter.isSet())
		{
			return this->setter(this, value);
		}
		
		//otherwise, set value
		this->value = value;
	}
	
	template<typename PropertyType> Bound<PropertyType> &
	Bound<PropertyType>::convert(BaseConverter<PropertyType> *converter)
	{
		//set converter
		this->_converter = converter;
		
		//if this is bound, update value
		if (this->boundTo) this->updated(this->boundTo);
		
		//return *this
		return *this;
	}

	/**
	 \fn _isBindable(PropertyType p);
	 \brief Returns false, as the property is not a pointer, and isBindable
	 is meant to describe if the property is a BindableObject pointer.
	 */
	template <typename PropertyType> bool _isBindable(PropertyType p)
	{
		//return false (it is not a pointer)
		return false;
	}
	
	/**
	 \fn _isBindable(PropertyType p);
	 \brief Returns NULL, as the property is not a pointer, and _getBindable
	 is meant to return a pointer to the BindableObject object.
	 */
	template <typename PropertyType> BindableObject *_getBindable(PropertyType p)
	{
		return NULL;
	}
	
	/**
	 \fn _isBindable(PropertyType *p);
	 \brief Returns true if p is a BindableObject object.
	 */
	template <typename PropertyType> bool _isBindable(PropertyType *p)
	{
		BindableObject *b = dynamic_cast<BindableObject *> (p);
		if (b) return true;
		return false;
	}
	
	/**
	 \fn _getBindable(PropertyType *p);
	 \brief Returns the property if it is a BindableObject object. Otherwise returns
	 NULL.
	 */
	template <typename PropertyType> BindableObject *_getBindable(PropertyType *p)
	{
		return dynamic_cast<BindableObject *> (p);
	}
	
	
	template<typename PropertyType> bool
	Bound<PropertyType>::isBindable()
	{
		//return if the value is bindable.
		return _isBindable(this->get());
	}
	
	template<typename PropertyType> BindableObject *
	Bound<PropertyType>::getBindable()
	{
		//return the BindableObject * value (or NULL if it isn't Bindable).
		return _getBindable(this->get());
	}
	
	template<typename PropertyType> bool
	Bound<PropertyType>::isWeakBind()
	{
		//if this is weakly bound, return true
		if (this->weakBind) return true;
		
		//return false otherwise
		return false;
	}
	
	template<typename PropertyType>
	inline Bound<PropertyType>::operator PropertyType() const
	{
		//return value
		return this->get();
	}
	
	template<typename PropertyType> PropertyType
	inline Bound<PropertyType>::operator -> ()
	{
		//return value
		return this->get();
	}
	
	template<typename PropertyType> bool
	Bound<PropertyType>::operator == (Bound<PropertyType> &p) const
	{
		if (this->get() == p.get()) return true;
		return false;
	}

	template<typename PropertyType> bool
	Bound<PropertyType>::operator == (PropertyType p) const
	{
		if (this->get() == p) return true;
		return false;
	}
	
	template<typename PropertyType>
	Bound<PropertyType>::~Bound()
	{

	}

} //end namespace

/**
 \fn operator<<(std::ostream &out, Bind::Bound<T> &property);
 \brief Allows Bound properties to be written to ostreams, provided that their
 value can be written.
 */
template <typename T> 
std::ostream &operator<< (std::ostream &out, Bind::Bound<T> &property)
{
	//write to output
	out << property.get();
	
	//return the stream
	return out;
}

/**
 \fn operator==(T val, const Bind::Bound<T> &property);
 \brief Returns true if the value supplied and the value of the property 
 are equal.
 */
template<typename T>
bool operator==(const T val, const Bind::Bound<T> &property)
{
	if (val == property.get()) return true;
	return false;
}


#endif
